package com.example.oauth.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionException;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class SpELUtil {

    private static ExpressionParser parser = new SpelExpressionParser();

    private static ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();

    private SpELUtil() {
        // default constructor
    }

    /**
     * 获取spel值
     *
     * @param pjp
     * @return
     */
    public static String getScriptValue(ProceedingJoinPoint pjp, String script) {
        try {
            Map<String, Object> dataMap = null;

            // 获取目标参数
            Object[] args = pjp.getArgs();
            if (args != null && args.length > 0) {
                // 获取拦截方法参数
                String[] params = discoverer.getParameterNames(getTargetMethod(pjp));

                // 组装spel参数
                dataMap = new HashMap<>(16);
                for (int len = 0; len < params.length; len++) {
                    dataMap.put(params[len], args[len]);
                }
            }

            // eval spel
            return getValue(dataMap, script, String.class);
        } catch (Exception ex) {
            throw new RuntimeException(String.format("解析spel[%s]失败", script), ex);
        }
    }

    /**
     * Spel 规则匹配
     *
     * @param data   数据
     * @param script 规则表达式
     * @param <T>
     * @return 是否匹配
     */
    public static <T> boolean matchRule(T data, String script) {
        Boolean matchResult = doGetValue(data, script, Boolean.class);
        if (matchResult == null) {
            return false;
        }
        return matchResult;
    }

    /**
     * Spel表达式计算
     *
     * @param dataMap
     * @param script  规则表达式
     * @param <T>
     * @return 是否匹配
     */
    public static <T> T getValue(Map<String, Object> dataMap, String script, Class<T> resultClass) {
        return doGetValue(dataMap, script, resultClass);
    }

    /**
     * Spel表达式计算
     *
     * @param rootObject
     * @param script     规则表达式
     * @param <T>
     * @return 是否匹配
     */
    public static <T> T getValue(Object rootObject, String script, Class<T> resultClass) {
        return doGetValue(rootObject, script, resultClass);
    }

    /**
     * Spel表达式计算(无参数)
     *
     * @param script 规则表达式
     * @param <T>
     * @return 是否匹配
     */
    public static <T> T getValue(String script, Class<T> resultClass) {
        return doGetValue(null, script, resultClass);
    }

    /**
     * Spel表达式计算
     *
     * @param data
     * @param script 规则表达式
     * @param <T>
     * @return 是否匹配
     */
    private static <T> T doGetValue(Object data, String script, Class<T> resultClass) {
        try {
            if (StringUtils.isEmpty(script)) {
                return null;
            }
            // 解析spel表达式
            Expression expression = parser.parseExpression(script);
            if (data != null) {
                // 设置数据
                StandardEvaluationContext context = new StandardEvaluationContext();
                if (data instanceof Map) {
                    if (MapUtils.isEmpty((Map<String, Object>) data)) {
                        return null;
                    }
                    context.setVariables((Map<String, Object>) data);
                } else {
                    context.setRootObject(data);
                }
                return expression.getValue(context, resultClass);
            } else {
                return expression.getValue(resultClass);
            }
        } catch (ExpressionException e) {
//            LogUtil.error(String.format("rule >>%s, error >>%s", script, e.getMessage()), e);
            return null;
        }
    }

    /**
     * 校验Spel表达式
     *
     * @param script 规则表达式
     * @return 如果返回null表达式有效, 否者返回：错误消息
     */
    public static String checkRule(String script) {
        try {
            parser.parseExpression(script);
        } catch (ExpressionException e) {
            return e.getMessage();
        }
        return null;

    }

    /**
     * 获取目标方法
     */
    private static Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method agentMethod = methodSignature.getMethod();
        return pjp.getTarget().getClass().getMethod(agentMethod.getName(), agentMethod.getParameterTypes());
    }

}
